import json
from datetime import datetime
from sqlalchemy.orm import Session
from core.response import resp_500
from core.logger import logger
from app.monitor_metrics.models.prom_alarm_template import PromAlarmTemplate
from app.monitor_metrics.models.prom_alarm_strategy import PromAlarmStrategy
from app.monitor_metrics.models.prom_host_group import PromHostGroup
from app.monitor_metrics.models.prom_host_info import PromHostInfo
from app.monitor_metrics.models.prom_network_group import PromNetworkGroup
from app.monitor_metrics.models.prom_alarm_rule import PromAlarmRule
from app.monitor_metrics.models.prom_alarm_group import PromAlarmGroup
from app.monitor_metrics.models.prom_alarm_set import PromAlarmSet
from app.monitor_metrics.handlers.prom_alarm_set_handler import PromAlarmSetHandler
from app.monitor_metrics.handlers.prom_alarm_group_handler import PromAlarmGroupHandler
from app.monitor_metrics.handlers.prom_alarm_rule_handler import PromAlarmRuleHandler
from app.monitor_metrics.handlers.prom_alarm_strategy_handler import PromAlarmStrategyHandler
from app.audit.handlers.audit_event_handler import AuditEventHandler
from app.monitor_metrics.utils import PromCommonUtils
from app.celery_task.models.celery_task import CeleryTask

class PromAlarmTemplateHandler():
    """  AlarmTemplate Handler
    """
    def __init__(self):
        self.prom_alarm_group_handler = PromAlarmGroupHandler()
        self.prom_alarm_rule_handler = PromAlarmRuleHandler()
        self.prom_alarm_strategy_handler = PromAlarmStrategyHandler()
        self.prom_alarm_set_handler = PromAlarmSetHandler()



    def get_alarm_template_search(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段模糊匹配, 获取告警模板信息
        """
        try:
            data_info = {field: value, "type": "bind"}
            order_info = {"classes": "asc", "name": "asc"}
            result = PromAlarmTemplate.filter_by_field_search(db, page, per_page, data_info, order_info)
            if not result:
                logger.warning(f"get alarm template search, result is null => {field}:{value}")
                return False, None

        except Exception as e:
            message = f"get alarm template search by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, result


    def get_alarm_template_match(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段完全匹配, 获取告警模板信息
        """
        try:
            data_info = {
                field: value,
                "type": "bind"
            }
            result = PromAlarmTemplate.filter_by_field_match(db, page, per_page, data_info)
            if not result:
                logger.warning(f"get alarm template match, result is null => {field}:{value}")
                return False

        except Exception as e:
            message = f"get alarm template match by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return result



    def get_alarm_template_strategy(self, db: Session, uuid: int):
        """
        根据表字段完全匹配, 获取告警模板信息
        """
        try:
            if not PromAlarmTemplate.get_object_by_uuid(db, uuid):
                logger.error(f"get alarm template strategy, uuid:{uuid} not exist")
                return False, None

            alarm_template_info = PromAlarmTemplate.get_object_info_by_uuid(db, uuid)
            strategies_info = alarm_template_info["strategies"]
        except Exception as e:
            message = f"get alarm template strategy by uuid:{uuid} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, strategies_info


    def add_alarm_template(self, db: Session, data: dict):
        """
        创建告警模板
        """
        try:
            alarm_template_object = PromAlarmTemplate.get_object_by_name(db, data["name"])
            if alarm_template_object:
                # 如果存在， 则异常返回
                logger.error(f"add alarm template failed,{data['name']} was exist")
                return False

            result = PromAlarmTemplate.add(db, data)
            if not result:
                logger.error(f"add alarm template to database failed")
                return False
        except Exception as e:
            message = f"add alarm template error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True



    def update_alarm_template(self, db: Session, data: dict):
        """
        修改告警模板信息
        """
        try:
            task_data = {}
            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, data["uuid"])
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(f"update alarm template failed, uuid:{data['uuid']} not exist")
                return False, task_data

            # 是否有告警策略更新
            if alarm_template_object.is_update:
                # 启动异步任务
                task_data = self.task_update_alarm_template_start(db, data)
                if not task_data:
                    logger.error(f"task update alarm template start failed")
                    return False, task_data

                logger.info(f"task update alarm template start success")
                
                # 推送到事件审计
                self.push_to_audit_event(db, "update_alarm_template", data)

            #     # 查询此告警模板绑定的主机组, 更新绑定
            #     host_groups_info = PromHostGroup.get_object_info_by_template(db, data["uuid"])
            #     for host_group_info in host_groups_info:
            #         # 这里可能拿不到所有的主机信息，后面再调试
            #         for host_info in host_group_info["hosts"]:
            #             result = self.host_update_bind_template(db, host_info["uuid"], [data['uuid']])
            #             if not result:
            #                 logger.error(f"update alarm template:{data['uuid']} failed, to alarm group")
            #                 return False

            # 更新数据库中的告警模板
            data["is_update"] = False
            result = PromAlarmTemplate.update(db, data)
            if not result:
                logger.error(f"update alarm template to database failed")
                return False, task_data
        except Exception as e:
            message = f"update alarm_template error "
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        logger.info(f"update alarm template:{data['uuid']} success")
        return True, task_data


    def task_update_alarm_template_handle(self, db: Session, data: dict):
        """
        更新告警模板异步任务的处理函数
        """
        # 查询此告警模板绑定的主机组, 更新绑定
        host_groups_info = PromHostGroup.get_object_info_by_template(db, data["uuid"])
        for host_group_info in host_groups_info:
            # 这里可能拿不到所有的主机信息，后面再调试
            for host_info in host_group_info["hosts"]:
                result = self.host_update_bind_template(db, host_info["uuid"], [data['uuid']])
                if not result:
                    logger.error(f"host:{host_info['uuid']} update alarm template:{data['uuid']} failed")
                    return False

        logger.info(f"update alarm template:{data['uuid']} success")
        return True


    def task_update_alarm_template_start(self, db: Session, data: dict):
        """
        启动更新模板的异步任务
        """
        # 启动异步任务
        from app.monitor_metrics.task import prom_update_alarm_template_async
        # 判断任务是否已经启动，控制只有一个任务在跑。 如果任务可以多个同时进行，则无需这一步判断。
        task_name = prom_update_alarm_template_async.name.split(".")[-1]
        if CeleryTask.is_progress_on_name(db, task_name):
            logger.error(f"task: {task_name} was stared")
            return False

        task = prom_update_alarm_template_async.delay(data)
        task_data = {
            "name": task_name,
            "tid": task.id,
            "state": task.state
        }

        logger.info(f"add task: {task_name} success")
        return task_data



    def delete_alarm_template(self, db: Session, data: dict):
        """
        删除告警模板信息
        """
        try:
            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, data['uuid'])
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(f"delete alarm template failed, uuid:{data['uuid']} not exist")
                return False

            # 模板中还有告警策略，不能删除. 只是个安全限制，增加删除操作步骤，防止误删
            if len(alarm_template_object.strategies) != 0:
                #
                logger.error(f"delete alarm template failed, has alarm strategy in it")
                return False

            # 告警模板还有绑定的主机组,不能删除
            host_groups_info = PromHostGroup.get_object_info_by_template(db, data["uuid"])
            if len(host_groups_info) != 0:
                logger.error(f"delete alarm template failed, has bind host group")
                return False

            # 删除告警模板
            PromAlarmTemplate.delete_object_by_uuid(db, data['uuid'])
        except Exception as e:
            message = f"delete alarm template error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True





    #### 主机与告警模板的操作 ####
    def host_unbound_template(self, db: Session, host_uuid: int, templates_uuid: list[int]):
        """
        主机解绑告警模板
        """
        host_info = PromHostInfo.get_object_info_by_uuid(db, host_uuid)
        for template_uuid in templates_uuid:
            template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)

            # 删除告警规则组内的所有告警规则
            alarm_group_name = PromCommonUtils.get_alarm_group_name(host_info, template_info)
            if not PromAlarmGroup.get_object_by_name(db, alarm_group_name):
                logger.warning(f"host_unbound_template: alarm_group_name:{alarm_group_name} not exist")
                continue

            alarm_group_info = PromAlarmGroup.get_object_info_by_name(db, alarm_group_name)
            for alarm_rule_info in alarm_group_info["alarm_rules"]:
                PromAlarmRule.delete_object_by_uuid(db, alarm_rule_info["uuid"])

            # 再删除告警规则组
            PromAlarmGroup.delete_object_by_uuid(db, alarm_group_info['uuid'])

        # 告警规则集更新到prometheus rule
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)
        result = self.prom_alarm_set_handler.alarm_set_sync_prometheus_rule(db, alarm_set_info["uuid"])
        if not result:
            logger.error(f"host_unbound_template: update alarm_set:{alarm_set_info['name']} failed")
            return False

        # 推送到事件审计
        data = {
            "host_uuid": host_uuid,
            "templates_uuid": templates_uuid
        }
        self.push_to_audit_event(db, "host_unbound_template", data)
        logger.info(f"host_info:{host_info['name']} unbound {templates_uuid} success")
        return True


    def host_template_to_alarm_group(self, db: Session, host_info: dict, template_info: dict):
        """
        主机绑定一个告警模板，生产一个告警组
        """
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)

        alarm_group_data = {
            "name": PromCommonUtils.get_alarm_group_name(host_info, template_info),
            "alarm_set_uuid": alarm_set_info["uuid"],
            "comment": f"告警模板【{template_info['name']}】 绑定 主机组【{host_info['host_group']}】 生成的告警规则组"
        }

        alarm_group_object = PromAlarmGroup.get_object_by_name(db, alarm_group_data["name"])
        if alarm_group_object:
            # 如果存在， 则异常返回
            logger.info(f"host_template_to_alarm_group,{alarm_group_data['name']} was exist")
            return True

        result = self.prom_alarm_group_handler.add_alarm_group_to_database(db, alarm_group_data)
        if not result:
            logger.error(f"host_template_to_alarm_group: create alarm group:{alarm_group_data['name']} failed")
            return False

        return True


    def host_bind_template(self, db: Session, host_uuid: int, templates_uuid: list[int]):
        """
        主机绑定告警模板
        """
        if not templates_uuid:
            logger.warning(f"host_bind_template nothing, templates_uuid is {templates_uuid}")
            return True

        host_info = PromHostInfo.get_object_info_by_uuid(db, host_uuid)

        # 获取主机的规则集.
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)

        # 批量创建模板的告警规则
        for template_uuid in templates_uuid:
            template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)

            # 创建主机的规则组, 一个模板 对应 一个告警组
            result = self.host_template_to_alarm_group(db, host_info, template_info)
            if not result:
                logger.error(f"host_bind_template: host template create alarm group failed")
                return False

            # 遍历告警模板的告警策略，来创建告警规则
            for strategy_info in template_info["strategies"]:
                # 创建告警规则
                result = self.prom_alarm_strategy_handler.host_strategy_to_alarm_rule(db, host_info, template_info, strategy_info)
                if not result:
                    logger.error(f"host_bind_template: create alarm rule:{strategy_info['name']} failed")
                    return False

        # 告警规则集更新到prometheus rule
        result = self.prom_alarm_set_handler.alarm_set_sync_prometheus_rule(db, alarm_set_info["uuid"])
        if not result:
            self.host_unbound_template(db, host_uuid, templates_uuid)
            logger.error(f"host_bind_template: update alarm_set:{alarm_set_info['name']} failed")
            return False

        # 推送到事件审计
        data = {
            "host_uuid": host_uuid,
            "templates_uuid": templates_uuid
        }
        self.push_to_audit_event(db, "host_bind_template", data)

        logger.info(f"host_info:{host_info['name']} bind {templates_uuid} success")
        return True



    def host_update_bind_template(self, db: Session, host_uuid: int, templates_uuid: list[int]):
        """
        更新主机绑定的告警模板
        """
        host_info = PromHostInfo.get_object_info_by_uuid(db, host_uuid)

        # 更新每个模板生成的告警规则
        for template_uuid in templates_uuid:
            template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)

            # 先删除告警规则组内的所有告警规则，然后再重新添加
            alarm_group_name = PromCommonUtils.get_alarm_group_name(host_info, template_info)
            if not PromAlarmGroup.get_object_by_name(db, alarm_group_name):
                logger.error(f"host_update_bind_template: host_uuid:{host_uuid} template_uuid:{template_uuid} are no binding")
                continue

            alarm_group_info = PromAlarmGroup.get_object_info_by_name(db, alarm_group_name)
            for alarm_rule_info in alarm_group_info["alarm_rules"]:
                PromAlarmRule.delete_object_by_uuid(db, alarm_rule_info["uuid"])

            # 遍历告警模板的告警策略，来创建告警规则
            for strategy_info in template_info["strategies"]:
                # 创建告警规则
                result = self.prom_alarm_strategy_handler.host_strategy_to_alarm_rule(db, host_info, template_info, strategy_info)
                if not result:
                    logger.error(f"host_update_bind_template: create alarm rule:{strategy_info['name']} failed")
                    return False
                
            logger.info(f"host_update_bind_template: host_uuid:{host_uuid} template_uuid:{template_uuid} update success")

        # 告警规则集更新到prometheus rule
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)
        result = self.prom_alarm_set_handler.alarm_set_sync_prometheus_rule(db, alarm_set_info["uuid"])
        if not result:
            logger.error(f"host_update_bind_template: update alarm_set:{alarm_set_info['name']} failed")
            return False

        # 推送到事件审计
        data = {
            "host_uuid": host_uuid,
            "templates_uuid": templates_uuid
        }
        self.push_to_audit_event(db, "host_update_bind_template", data)

        logger.info(f"host_info: host_uuid:{host_uuid} templates_uuid:{templates_uuid} all update success")
        return True







    #### 网络探测组与告警模板的操作 ####
    def network_group_to_alarm_group(self, db: Session, network_group_info: dict, template_info: dict):
        """
        网络探测组绑定模板，创建告警规则组
        """
        alarm_set_name = PromCommonUtils.get_alarm_set_name(network_group_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)

        alarm_group_data = {
            "name": PromCommonUtils.get_alarm_group_name(network_group_info, template_info),
            "alarm_set_uuid": alarm_set_info["uuid"],
            "comment": f"告警模板【{template_info['name']}】 绑定 网络探测组【{network_group_info['name']}】 生成的告警规则组"
        }

        result = self.prom_alarm_group_handler.add_alarm_group_to_database(db, alarm_group_data)
        if not result:
            logger.error(f"network_group_to_alarm_group: create alarm group {alarm_group_data['name']} failed")
            return False

        return True


    def network_group_bind_template(self, db: Session, network_group_uuid: int, template_uuid: int):
        """
        网络探测组绑定模板
        """

        # 获取网络探测组的规则集.
        network_group_info = PromNetworkGroup.get_object_info_by_uuid(db, network_group_uuid)
        alarm_set_name = PromCommonUtils.get_alarm_set_name(network_group_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)

        # 创建规则组, 一个模板 对应 一个告警规则组
        template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)
        result = self.network_group_to_alarm_group(db, network_group_info, template_info)
        if not result:
            logger.error(f"network_group_bind_template: create alarm group:{network_group_info['name']} failed")
            return False

        # 遍历告警模板的告警策略，来创建告警规则
        for strategy_info in template_info["strategies"]:
            # 创建告警规则
            result = self.prom_alarm_strategy_handler.network_group_strategy_to_alarm_rule(db, network_group_info, template_info, strategy_info)
            if not result:
                logger.error(f"network_group_bind_template: create alarm rule:{strategy_info['name']} failed")
                return False

        # 告警规则集更新到prometheus rule
        result = self.prom_alarm_set_handler.alarm_set_sync_prometheus_rule(db, alarm_set_info["uuid"])
        if not result:
            self.network_group_unbound_template(db, network_group_uuid, template_uuid)
            logger.error(f"network_group_bind_template: update alarm_set:{alarm_set_info['name']} failed")
            return False

        logger.info(f"network_group:{network_group_info['name']} bind {template_info['name']} success")
        return True



    def network_group_unbound_template(self, db: Session, network_group_uuid: int, template_uuid: int):
        """
        网络探测组解绑告警模板
        """
        network_group_info = PromNetworkGroup.get_object_info_by_uuid(db, network_group_uuid)
        template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)

        # 删除告警规则组内的所有告警规则
        alarm_group_name = PromCommonUtils.get_alarm_group_name(network_group_info, template_info)
        if not PromAlarmGroup.get_object_by_name(db, alarm_group_name):
            logger.warning(f"network_group_unbound_template: alarm_group_name:{alarm_group_name} not exist")
            return True

        alarm_group_info = PromAlarmGroup.get_object_info_by_name(db, alarm_group_name)
        for alarm_rule_info in alarm_group_info["alarm_rules"]:
            PromAlarmRule.delete_object_by_uuid(db, alarm_rule_info["uuid"])

        # 再删除告警规则组
        PromAlarmGroup.delete_object_by_uuid(db, alarm_group_info['uuid'])

        # 告警规则集更新到prometheus rule
        alarm_set_name = PromCommonUtils.get_alarm_set_name(network_group_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)
        result = self.prom_alarm_set_handler.alarm_set_sync_prometheus_rule(db, alarm_set_info["uuid"])
        if not result:
            logger.error(f"network_group_unbound_template: update alarm_set:{alarm_set_info['name']} failed")
            return False

        logger.info(f"network_group:{network_group_info['name']} unbound {template_info['name']} success")
        return True



    def network_group_update_bind_template(self, db: Session, network_group_uuid: int, template_uuid: int):
        """
        网络探测组更新绑定的模板
        """
        network_group_info = PromNetworkGroup.get_object_info_by_uuid(db, network_group_uuid)
        template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)


        # 先删除告警规则组内的所有告警规则，然后再重新添加
        alarm_group_name = PromCommonUtils.get_alarm_group_name(network_group_info, template_info)
        if not PromAlarmGroup.get_object_by_name(db, alarm_group_name):
            logger.error(f"network_group_update_bind_template: {network_group_info['name']}:{template_info['name']} are no binding")
            return False

        alarm_group_info = PromAlarmGroup.get_object_info_by_name(db, alarm_group_name)
        for alarm_rule_info in alarm_group_info["alarm_rules"]:
            PromAlarmRule.delete_object_by_uuid(db, alarm_rule_info["uuid"])

        # 遍历告警模板的告警策略，来创建告警规则
        for strategy_info in template_info["strategies"]:
            # 创建告警规则
            result = self.prom_alarm_strategy_handler.network_group_strategy_to_alarm_rule(db, network_group_info, template_info, strategy_info)
            if not result:
                logger.error(f"network_group_update_bind_template: create alarm rule:{strategy_info['name']} failed")
                return False

        # 告警规则集更新到prometheus rule
        alarm_set_name = PromCommonUtils.get_alarm_set_name(network_group_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)
        result = self.prom_alarm_set_handler.alarm_set_sync_prometheus_rule(db, alarm_set_info["uuid"])
        if not result:
            logger.error(f"network_group_update_bind_template: update alarm_set:{alarm_set_info['name']} failed")
            return False

        logger.info(f"network_group:{network_group_info['name']} updatebind {template_info['name']} success")
        return True
    
    
    
    def push_to_audit_event(self, db: Session, event_name: str, data: dict):
        audit_event_handler = AuditEventHandler()
        event_data = {
            "event_name": event_name,
            "event_type": "monitor_metrics",
            "is_notice": True,
            "comment": str(data)
        }
        audit_event_handler.add_audit_event(db, event_data)
        return True
